'''
Created on Mar 16, 2011

@author: yc30v1m
'''
import xmlrpclib
import datetime
import base64



class TestlinkAPIClient(object):
    '''
    classdocs
    '''
    # substitute your server URL Here
    SERVER_URL = "http://d3162364/testlink/lib/api/xmlrpc.php"
    
    '''
    Do not change anyting below this line
    User Configurations completed
    '''
    
    DEFAULT_PROJECT_NAME_LIKE = ''
    DEFAULT_TESTPLAN_NAME_LIKE = ''
    DEFAULT_BUILD_NAME_LIKE = ''
    DEFAULT_ATTACHED_SCRIPT_FILE_NAME = ''
    #Project Related
    testLinkSignature = 'tl'
    testCaseGlueChar = '-'
    testLinkAttachedScriptFileName = ''
    
    tProjects = ''
    tProjectName = ''
    tProjectId = ''
    tProjectPrefix = ''
    tProject = ''
    #Test Cases
    tTestCases = ''
    tTestCaseIds = ''
    tTestCasesWithScriptAttachment = []
    #TestPlan Related
    tTestPlans = ''
    tTestPlanName = ''
    tTestPlanId = ''
    #Build Related
    tBuilds = ''
    tBuildName = ''
    tBuildId = ''
    
    #CONSTANTS
    devKeyName = "devKey"
    
    def __init__(self, devKey, tlUrl=None):
        if tlUrl != None:
            self.SERVER_URL = tlUrl
        else:
            self.server = xmlrpclib.ServerProxy(self.SERVER_URL, transport=None, encoding="UTF-8", verbose=0, allow_none=1, use_datetime=1)
        #self.server = xmlrpclib.Server(self.SERVER_URL)
        print devKey + " dev Key"
        self.devKey = devKey
 
    def runAutoSetup(self):
        self.checkAndCreateBuildForToday()
        self.getLatestBuildForTestPlan()
 
    def reportTCResult(self, tcid, status, CheckValidScriptAttachment = True):
        checkPyValid = True
        if CheckValidScriptAttachment == True:
            checkPyValid = self.tTestCasesWithScriptAttachment.__contains__(tcid)
        if self.checkIfTestCaseIdExistsInTestPlan(tcid) == True:
            if checkPyValid == True:
                data = {}
                data ["buildid"] = self.tBuildId
                data["testplanid"] = self.tTestPlanId
                data["status"] = status
                data["testcaseexternalid"] = tcid
                return self._makeXmlRpcCall("reportTCResult", data)
        print 'TestCase result Not Updated in Testlink'
        return False
    
    def checkIfTestCaseIdExistsInTestPlan(self,testCaseId):
        if len(self.tTestCaseIds) == 0:
            self.getTestCaseIdsForTestPlan()
        return self.tTestCaseIds.__contains__(testCaseId)
    
    def getTestCasesForTestPlan(self, testExecutionType = "Automation"):
        if self.tTestPlanId == '':
            self.getProjectTestPlanId()
        data = {}
        data["testplanid"] = self.tTestPlanId
        if testExecutionType == "Automation":
            data["executiontype"] = 2
        if testExecutionType == "Manual":
            data["executiontype"] = 1
        self.tTestCases = self._makeXmlRpcCall("getTestCasesForTestPlan", data)
        if self.tTestCases == '':
            print 'ERROR!!!'
            print 'ERROR! There are no TestCases in TestPlan - '
            print 'Please consider looking at TestPlan for Automation / Manual TestCases'
            print 'ERROR'
        return self.tTestCases
    
    def getTestCaseIdsForTestPlan(self):
        self.tTestCaseIds = []
        if self.tTestCases == '':
            self.getTestCasesForTestPlan()
        try:
            for tc in self.tTestCases.keys():
                self.tTestCaseIds.append(self.tProjectPrefix + self.testCaseGlueChar + self.tTestCases[tc]['external_id'])
        except Exception, v:
            print 'Exception occured ', str(v)
        return self.tTestCaseIds 
    
    def checkAndCreateBuildForToday(self):
        today = datetime.date.today()
        self.setBuildName(str(today))
        tlState = self.getTestPlanBuildId()
        retState = False
        if tlState == False:
            args = {}
            args["testplanid"] = self.tTestPlanId
            args["buildname"] = str(today)
            args["buildnotes"] = 'Created by Automation Script on ' + str (today)
            self._makeXmlRpcCall('createBuild', args)
            self.setBuildName(str(today))
            self.getTestPlanBuildId()
            retState = True
        return retState
    
    
        
    def getTestCase(self):
        data = {"devKey":self.devKey,"testcasename":"Demo Test case for Test","testsuitename":"Sample1","testprojectname":"PSP"}
        return self.server.tl.getTestCaseIDByName(data)
    
    def getInfo(self):
        return self.server.tl.about()
    
    def getProjects(self):
        data = {}
        self.tProjects = self._makeXmlRpcCall("getProjects", data)
        return self.tProjects
    
    '''
    setups the project and Project perfix 
    also setups the project id & name
    '''
    def setupProjectDetails(self):
        if self.tProjectId == '':
            self.getProjectId()
        self.tProject = self._objectFinder(self.tProjectName, self.tProjects)
        self.tProjectPrefix = self._finder(self.tProject, 'prefix')
        self.tProjectName = self._finder(self.tProject, 'name')
            
    def getProjectId(self):
        if self.tProjectId == '':
            if self.tProjectName == '':
                self.setProjectName()
            if self.tProjects == '':
                self.getProjects()
            self.tProjectId = self._idFinder(self.tProjectName, self.tProjects)
            self.setupProjectDetails()
        return self.tProjectId
    
    def getProjectTestPlans(self):
        if self.tProjectId == '':
            self.getProjectId()
        data = {"devKey":self.devKey,"testprojectid":self.tProjectId}
        #self.tTestPlans = self.server.tl.getProjectTestPlans(data)
        self.tTestPlans = self._makeXmlRpcCall("getProjectTestPlans", data)
        return self.tTestPlans
    
    def getProjectTestPlanId(self):
        if self.tTestPlanId == '':
            if self.tTestPlans == '':
                self.getProjectTestPlans()
            if self.tTestPlanName == '':
                self.setTestPlanName()
            self.tTestPlanId = self._idFinder(self.tTestPlanName, self.tTestPlans)
        return self.tTestPlanId
    
    def getTestPlanBuilds(self):
        if self.tTestPlanId == '':
            self.getProjectTestPlanId()
        data = {"devKey":self.devKey,"testplanid":self.tTestPlanId}
        #self.tBuilds = self.server.tl.getBuildsForTestPlan(data)
        self.tBuilds = self._makeXmlRpcCall("getBuildsForTestPlan", data)
        return self.tBuilds
    
    '''
    Gets the LatestBuild from TL and overrides all the defaults for Build
    Sets up the Build Name & Build Id
    '''    
    def getLatestBuildForTestPlan(self):
        if self.tTestPlanId == '':
            self.getProjectTestPlanId()
        data = {"testplanid":self.tTestPlanId}
        self.tLatestBuild  =  self._makeXmlRpcCall("getLatestBuildForTestPlan", data)
        self.tBuildId = self.tLatestBuild['id']
        self.setBuildName(self.tLatestBuild['name'])
        return self.tLatestBuild
    
    def getTestPlanBuildId(self):
        if self.tBuilds == '':
            self.getTestPlanBuilds()
        if self.tBuildName == '':
            self.setBuildName()
        self.tBuildId = self._idFinder(self.tBuildName, self.tBuilds)
        return self.tBuildId
        
    def setProjectName(self, projectName = DEFAULT_PROJECT_NAME_LIKE):
        self.tProjectName = projectName        
        
    def setTestPlanName(self,testPlanName = DEFAULT_TESTPLAN_NAME_LIKE):
        self.tTestPlanName = testPlanName
        
    def setBuildName(self,buildName = DEFAULT_BUILD_NAME_LIKE):
        self.tBuildName = buildName
        
    def setAutomationScriptFileName(self,attachedScriptFileName=DEFAULT_ATTACHED_SCRIPT_FILE_NAME):
        self.testLinkAttachedScriptFileName = attachedScriptFileName
        
    def getTestCaseCustomFieldDesignValue(self, tcid):
        data = {}
        data["devKey"] = self.devKey
        data["testcaseexternalid"] = "PSP-" + tcid
        data["testprojectid"] = self.tProjectId
        data["customfieldname"]='autosteps';
        data["details"]='simple';
        return self._makeXmlRpcCall("getTestCaseCustomFieldDesignValue", data)
    
    def getTestCaseAttachments(self,tcid):
        if self.tTestCaseIds.__contains__(tcid):
            try:
                data = {}
                data["testcaseexternalid"] = tcid
                return self._makeXmlRpcCall("getTestCaseAttachments", data)
            except Exception, v:
                print 'Exception occured in getTestCaseAttachments: ' + str(v)
        else:
            print 'Not a Valid TestCase to retrive Script'
            return False
    
    def getTestScript(self,testCaseId):
        attachments = self.getTestCaseAttachments(testCaseId)
        if self.testLinkAttachedScriptFileName == '':
            self.setAutomationScriptFileName()
        latestTimeStamp = ''
        content = 'NO SCRIPT'
        try:
            for attachment in attachments.keys():
                if self._hasNeedle(attachments[attachment]['name'], self.testLinkAttachedScriptFileName) == True:
                    tempValue = attachments[attachment]['date_added']
                    if latestTimeStamp == '':
                        latestTimeStamp = tempValue
                    if latestTimeStamp <= tempValue:
                        latestTimeStamp = tempValue
                        content = base64.b64decode(attachments[attachment]['content'])
                        self.tTestCasesWithScriptAttachment.append(testCaseId)
        except:
            print 'Automation Script Attachments Not found with TestCase : ' + str(testCaseId)
        return content
    
    '''
    Generic function to retrive the values from TestLink XMLRPC response
    '''
    def _idFinder(self,needle,listItems,nameKey="name",idKey="id"):
        foundId = False
        x = self._objectFinder(needle, listItems, nameKey)
        if x != 'Nothing Found in List':
            foundId = self._finder(x, idKey)
        else:
            print 'Error Occured Unable to find Id'
            print 'Tyring to find : ' + str(needle)
            print 'and Tag Value : ' + str(nameKey)
            print 'In Below List Items : '
            print listItems
        return foundId
    
    ''' 
    Searches a Key in the list Objects and returns the Object if the Key 
    is found in the object otherwise retuns a string
    '''
    def _objectFinder(self,needle,listItems,keyToUse="name"):
        foundObj = 'Nothing Found in List'
        for listKey in range(len(listItems)):
            try:
                tempName = listItems[listKey][keyToUse]
                if self._hasNeedle(tempName, needle):
                    foundObj = listItems[listKey]
            except KeyError:
                print "Search Key not Found, Searched for : " +  keyToUse
        return foundObj
    
    def _hasNeedle(self,pstring,needle):
        if pstring.find(needle) >= 0:
            return True
        return False        
    
    def _finder(self,listItems, firstKey = '' , secondKey = ''):
        if firstKey == '':
            print 'Error Occured in Finder'
            print "First key is blank Please Check "
            return "First key is blank Please Check "
        if secondKey == '':
            return listItems[firstKey]
        return listItems[firstKey][secondKey]
    
    def _makeXmlRpcCall(self, methodName, data):
        result = "Unable to execute Function"
        data[self.devKeyName] = self.devKey
        try:
            exec "result = self.server." + self.testLinkSignature + "." + methodName + "(data)"
        except Exception, v:
            print "ERROR in _makeXmlRpcCall: ", str(v)
        return result
        #nothing

# substitute your Dev Key Here
#client = TestlinkAPIClient("b7b4ba2d19635872533d1925a091e92c")
#client = TestlinkAPIClient("56ba98ccb8727d29f7e4dbcb8468ff5c")
#client.runAutoSetup()
# get info about the server
#print client.getProjects()
#print client.getProjects()
#client.setupProjectDetails()
#client.getLatestBuildForTestPlan()
#print client.getProjectTestPlans()
#print client.getProjectTestPlanId()
#print client.getTestPlanBuilds()
#print client.getTestPlanBuildId()
#print client.checkAndCreateBuildForToday()
#print client.checkIfTestCaseIdExistsInTestPlan('PSP-1')
#client.getTestScript('PSP-3')
#print client.getTestScript('PSP-3')
#client.getTestScript('PSP-1')
#client.getTestScript('PSP-9')

#print client.reportTCResult('PSP-3','p')
    


#    print binascii.b2a_base64(x[tc1]['content'])
#print binascii.b2a_base64(x[1]['content'])
#client.getTestCasesForTestPlan()
#print client.getTestCaseIdsForTestPlan()
#print client.createBuildForToday()
#print client.getTestCaseIdsForTestPlan()
#print client.getTestCaseCustomFieldDesignValue("7")

#print client.getTestCase()
# Substitute for tcid and tpid that apply to your project
#result = client.reportTCResult(1132, 56646, "p")
# Typically you'd want to validate the result here and probably do something more useful with it
#print "reportTCResult result was: %s" %(result)